package com.aop;

import cn.hutool.json.JSONObject;
import com.aop.annotation.CircuitBreaker;
import com.aop.annotation.LogMethodExecution;
import com.cfg.EnvironmentService;
import com.websocket.WebSocketServer;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.annotation.Order;

import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.concurrent.TimeoutException;

/**
 * author: liubw
 * AOP 切面类，用于记录方法执行日志
 */
@Aspect
@Order(1)
public class LogMethodExecutionAspect {

    @Autowired
    private FindBussinessAnnoService findingBussinessAnnoService; // 用于获取业务描述
    @Autowired
    private WebSocketServer webSocketServer; // 用于发送WebSocket消息
    @Autowired
    private HttpServletRequest request; // 用于获取请求头信息
    @Autowired
    private EnvironmentService es;
    private LogMethodExecution lol;
    private static final Logger logger = LoggerFactory.getLogger(LogMethodExecutionAspect.class);
    private static final ThreadLocal<String> TRACE_ID = new ThreadLocal<>();
    private static final ThreadLocal<Long> METHOD_ID = new ThreadLocal<>();
    private static final ThreadLocal<Long> PREVIOUS_METHOD_ID = new ThreadLocal<>();
    private static final ThreadLocal<Stack<String>> CALL_STACK = new ThreadLocal<>();
    // 切点常量
    private final String pt = "execution(* com.log.*..*(..))";
//    private final String pt = "execution(* pro.java.*..*(..))";

    @Pointcut(pt)
    public void logMethodExecutionPointcut() {
    }

    // 在方法执行之前执行
    @Before("logMethodExecutionPointcut()")
    public void beforeMethodCall(JoinPoint joinPoint) {
        // 获取方法名
        String methodName = joinPoint.getSignature().getName();
        MethodExecutionContext.enterMethod(methodName);  // 记录方法进入
    }

    // 在方法执行之后执行
//    @After("logMethodExecutionPointcut()")
//    public void afterMethodCall(JoinPoint joinPoint) {
//        // 获取方法名
////        String methodName = joinPoint.getSignature().getName();
////        MethodExecutionContext.exitMethod();  // 输出方法进入
//    }

    // 拦截带有 @CircuitBreaker 注解的方法，发生异常时执行熔断方法
    @AfterThrowing(value = "@annotation(circuitBreaker)", throwing = "ex")
    public Object handleCircuitBreakerException(JoinPoint joinPoint, CircuitBreaker circuitBreaker, Throwable ex) {
        try {
            // 获取当前方法所属的目标对象（Service实例）
            Object targetService = joinPoint.getTarget();
            // 获取方法的名称
            String methodName = joinPoint.getSignature().getName();
            // 根据注解获取熔断方法
            String fallbackMethodName = circuitBreaker.value();
            if (fallbackMethodName == null) {
                throw new RuntimeException("方法异常：" + methodName + ",且没有配置熔断方法");
            }
            // 获取当前目标对象的 Class 对象
            Class<?> serviceClass = targetService.getClass();

            // 获取熔断方法的 Method 对象
            Method fallbackMethod = serviceClass.getMethod(fallbackMethodName);
            Object result = fallbackMethod.invoke(targetService);
            long currentMethodId = System.currentTimeMillis();
            // 获取前一个方法的ID
            Long previousMethodId = PREVIOUS_METHOD_ID.get();
            // 当前方法是被内部调用，记录方法名到调用栈
            CALL_STACK.get().push(fallbackMethodName);
            // 输出方法执行结果
            Object[] args = joinPoint.getArgs();
            MethodExecutionContext.enterMethod(fallbackMethodName);  // 记录熔断方法进入
            String logMessage = DemoteReturnDetails(methodName, fallbackMethodName, args,
                    result, currentMethodId, previousMethodId, null,
                    0);
            webSocketServer.sendAllMessage(logMessage);
            // 从调用栈中弹出当前方法
            CALL_STACK.get().pop();
            PREVIOUS_METHOD_ID.remove();
//            MethodExecutionContext.clear();
            // 通过反射调用熔断方法
            return logMessage;
        } catch (Exception e) {
            // 如果反射调用熔断方法失败，抛出异常
            throw new RuntimeException("Error invoking fallback method", e);
        }
    }

    // 捕获 Controller 和 Service 层的方法（包括接口及实现类的方法）
    @Around("logMethodExecutionPointcut()")
    public Object logMethodExecution(ProceedingJoinPoint joinPoint) throws Throwable {
        // 记录方法开始时间（毫秒级）
        long startTime = System.currentTimeMillis();
        long currentMethodId = System.currentTimeMillis();  // 当前方法的ID
        METHOD_ID.set(currentMethodId);
        // 获取当前方法签名
        Signature signature = joinPoint.getSignature();
        String methodName = signature.getName();
        String className = signature.getDeclaringTypeName();
        Map<String, String> annoMap = findingBussinessAnnoService.init(className, methodName);

        // 生成一个全局追踪ID
        if (TRACE_ID.get() == null) {
            TRACE_ID.set(UUID.randomUUID().toString());
        }
        Object[] args = joinPoint.getArgs();

        // 初始化方法调用栈
        if (CALL_STACK.get() == null) {
            CALL_STACK.set(new Stack<>());
        }

        // 当前方法是被内部调用，记录方法名到调用栈
        CALL_STACK.get().push(methodName);
//        MethodExecutionContext.enterMethod(methodName);
        // 获取前一个方法的ID
        Long previousMethodId = PREVIOUS_METHOD_ID.get();
        PREVIOUS_METHOD_ID.set(currentMethodId);
        // 获取请求 URL
        String requestUrl = request.getRequestURL().toString();
        // 获取请求头信息
        String DebuggerFlag = request.getHeader("Open-Debugger");
        String Token = request.getHeader("Token");
        // 执行目标方法并获取返回值
        Object result = null;
        String logMessage = null;
        try {
            // 抓取方法注解,输出业务描述
            result = joinPoint.proceed();  // 执行目标方法
            // 超时
            String timeout = annoMap.get("timeout");
            if (timeout != null && System.currentTimeMillis() > (Long.parseLong(timeout) + startTime)) {
                throw new TimeoutException("方法超时：" + methodName);
            }
            // 记录方法执行时间（毫秒）
            long endTime = System.currentTimeMillis();
            long executionTime = endTime - startTime;
            // 输出方法执行结果
            logMessage = logMethodReturnDetails(Token, methodName,
                    args, result, currentMethodId, previousMethodId, annoMap.get("desc"),
                    executionTime, DebuggerFlag, requestUrl);
            webSocketServer.sendAllMessage(logMessage);
        } catch (Throwable e) {
            // 记录方法执行时间（毫秒）
            long endTime = System.currentTimeMillis();
            long executionTime = endTime - startTime;
            logMessage = errorReturnDetails(Token, methodName,
                    args, result, currentMethodId, previousMethodId, annoMap.get("desc"),
                    executionTime, DebuggerFlag, requestUrl, e);
            // 从调用栈中弹出当前方法
            CALL_STACK.get().pop();
            PREVIOUS_METHOD_ID.remove();
            webSocketServer.sendAllMessage(logMessage);
            // 捕获异常并自定义返回值
            return handleException((Exception) e);  // 返回自定义异常处理结果
        }
        // 从调用栈中弹出当前方法
        CALL_STACK.get().pop();
        PREVIOUS_METHOD_ID.remove();
        // 如果需要写入文件
//        if (logMethodExecution.logToFile()) {
//            writeLogToFile(methodName, args, result, previousMethodId, currentMethodId);
//        }

        return result;
    }

    // 输出当前的调用栈
//    @After("logMethodExecutionPointcut()")
//    public void logMethodStack() {
//        List<String> callStack = MethodExecutionContext.getCurrentCallStack();
//        System.out.println("当前方法调用栈: " + callStack);
//    }

    private String logMethodReturnDetails(String token, String methodName, Object[] args, Object result,
                                          Long currentMethodId, Long previousMethodId, String desc,
                                          long mis, String DebuggerFlag, String requestUrl) {
        String traceId = TRACE_ID.get();
        Map<String, Object> map = new HashMap<String, Object>();
        map.put("logTag", "正常");
        map.put("请求地址", requestUrl);
        map.put("调用ID", traceId);
        map.put("方法ID", currentMethodId);
        map.put("上行方法ID", previousMethodId);
        map.put("方法名称", methodName);
        map.put("方法返回", result);
        map.put("业务", desc);
        map.put("执行时间", mis + "毫秒");
        map.put("调用栈", CALL_STACK.get().toString());// 输出当前方法调用栈
        if ("true".equals(DebuggerFlag)) {
            map.put("方法入参", argsToString(args));
            map.put("令牌", token);
        }
        JSONObject json = new JSONObject(map);
        logger.info(json.toString());
        return json.toString();
    }

    private String errorReturnDetails(String token, String methodName, Object[] args, Object result,
                                      Long currentMethodId, Long previousMethodId, String desc,
                                      long mis, String DebuggerFlag, String requestUrl, Throwable e) {
        String traceId = TRACE_ID.get();
        Map<String, Object> map = new HashMap<String, Object>();
        map.put("logTag", "异常");
        map.put("请求地址", requestUrl);
        map.put("调用ID", traceId);
        map.put("方法ID", currentMethodId);
        map.put("上行方法ID", previousMethodId);
        map.put("方法名称", methodName);
        map.put("业务", desc);
        map.put("执行时间", mis + "毫秒");
        map.put("异常信息", e.toString());
        map.put("调用栈", CALL_STACK.get().toString());// 输出当前方法调用栈
        if ("true".equals(DebuggerFlag)) {
            map.put("方法入参", argsToString(args));
            map.put("令牌", token);
        }
        JSONObject json = new JSONObject(map);
        logger.info(json.toString());
        return json.toString();
    }

    private String DemoteReturnDetails(String methodName, String fallbackMethodName, Object[] args, Object result,
                                       Long currentMethodId, Long previousMethodId, String desc,
                                       long mis) {
        SimpleDateFormat formatter = new SimpleDateFormat("dd-MM-yyyy HH:mm:ss");
        Date date = new Date();
        System.out.println(formatter.format(date));
        String traceId = TRACE_ID.get();
        Map<String, Object> map = new HashMap<String, Object>();
        map.put("logTag", "熔断");
        map.put("调用ID", traceId);
        map.put("方法ID", currentMethodId);
        map.put("降级前方法ID", previousMethodId);
        map.put("降级前方法名称", methodName);
        map.put("熔断时间", date);
        map.put("方法名称", fallbackMethodName);
        map.put("方法返回", result);
        map.put("业务", desc);
        map.put("参数", argsToString(args));
        map.put("执行时间", mis + "毫秒");
        map.put("调用栈", CALL_STACK.get().toString());// 输出当前方法调用栈
        JSONObject json = new JSONObject(map);
        logger.info(json.toString());
        return json.toString();
    }

    private String argsToString(Object[] args) {
        if (args == null || args.length == 0) {
            return "[]";
        }
        StringBuilder sb = new StringBuilder("[");
        for (Object arg : args) {
            sb.append(arg).append(", ");
        }
        sb.delete(sb.length() - 2, sb.length());  // 删除最后的逗号
        sb.append("]");
        return sb.toString();
    }

    // 异常处理逻辑，返回自定义输出
    private String handleException(Exception e) {
        // 根据异常类型或其他逻辑返回不同的响应
        System.out.println("异常信息: " + e.getMessage());
        return e.getMessage();
    }
}




